﻿namespace ChessToys
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Abstract implementation of <see cref="IMoveGenerator"/> prviding base functionality handling empty start positions.
    /// </summary>
    public abstract class AbstractMoveGenerator : IMoveGenerator
    {
        private static bool _filterCheckLeadingMoves = true;

        /// <inheritdoc />
        public ISet<Move> Generate(Board board, Position start)
        {
            // get piece from board
            IPiece piece = board[start];
            // check if at specified position stands a piece
            if (piece == null)
            {
                // if not -> excpetion
                throw new ArgumentException(String.Format("At position {0} is no piece.", start));
            }
            // generate moves for piece
            ISet<Move> moves = GenerateForPiece(board, start, piece);
            // filter check leading moves
            if (_filterCheckLeadingMoves)
            {
                FilterCheckLeadingMoves(moves, board, piece.Color);
            }
            // return moves
            return moves;
        }

        /// <summary>
        /// Filters the given moves. All moves are leading to check will be removed.
        /// Implementation detail: while FilterCheckLeadingMoves(ISet&lt;Move>, Board, Color) recursive calls are disabled statically;
        /// if not StackOverflow would be the result.
        /// </summary>
        /// <param name="moves">The moves.</param>
        /// <param name="board">The board.</param>
        /// <param name="color">The color.</param>
        private void FilterCheckLeadingMoves(ISet<Move> moves, Board board, Color color)
        {
            _filterCheckLeadingMoves = false;
            // check each move
            foreach (Move move in new HashSet<Move>(moves))
            {
                // temporary copy of board
                Board copy = new Board(board);
                // apply move to temporary copy
                copy.ApplyMove(move);
                // check for check
                if (CheckUtils.IsCheck(copy, color))
                {
                    // move leads to check -> remove it
                    moves.Remove(move);
                }
            }
            _filterCheckLeadingMoves = true;
        }

        /// <summary>
        /// Generates all possible moves for the piece.
        /// </summary>
        /// <param name="board">The board.</param>
        /// <param name="start">The start.</param>
        /// <param name="piece">The piece.</param>
        /// <returns>All possible moves for the piece.</returns>
        protected abstract ISet<Move> GenerateForPiece(Board board, Position start, IPiece piece);

        /// <summary>
        /// Crawls allong the board from the start position to the end position incrementing by the given increment.
        /// On each crawl step a new <see cref="Move"/> will created from start position to the current crawl position.
        /// If a friendly piece is found at the board no more moves will generated. If a adverse piece is found a
        /// final move will created to capture this piece.
        /// </summary>
        /// <param name="board">The board.</param>
        /// <param name="start">The start.</param>
        /// <param name="increment">The increment.</param>
        /// <param name="end">The end.</param>
        /// <returns>All moves while crawling the board.</returns>
        protected static ISet<Move> Crawl(Board board, Position start, Position increment, Position end)
        {
            ISet<Move> moves = new HashSet<Move>();
            Position current = start + increment;
            while (true)
            {
                // board borders
                if (current.X < 0 || current.X > board.Size - 1 || current.Y < 0 || current.Y > board.Size - 1)
                {
                    break;
                }
                // reaches current the end
                if (current.X == end.X || current.Y == end.Y)
                {
                    break;
                }
                // points current to a friend
                if (board[current] != null && board[current].Color == board[start].Color)
                {
                    break;
                }
                // add next posible move
                moves.Add(new Move(start, current));
                // points current to a enemy
                if (board[current] != null && board[current].Color != board[start].Color)
                {
                    break;
                }
                // increment the current crawl step
                current += increment;
            }
            return moves;
        }
    }
}
